<?php
/**************************************
 * View Class
 * Brainfucker.ru (c) Oleg Illarionov
 * Класс для компиляции шаблонов
 **************************************/
class tcompiler {
	var $object;
	var $class;
	var $methods;
	var $vars;
	var $tmp_path;
	
	function tcompiler(&$obj, $tmp_path='../tmp') {
		$this->object=&$obj;
		$this->class=get_class($this->object);
		$this->methods=get_class_methods($this->class);
		$this->vars=get_class_vars($this->class);
		$this->tmp_path=$tmp_path;
	}
	
	function get($path) {
		$tmp_path=$this->tmp_path.'/template_'.md5($path).'.php';
		//return $tmp_path;
		if (file_exists($tmp_path)) {
			# Время изменения файла  шаблона
			$tpl_change=filemtime($path);
			# время изменения кеша
			$tmp_change=filemtime($tmp_path);
			//echo $tmp_change.'---'.$tpl_change;
			if ($tpl_change==$tmp_change) {
				//echo 'FROM CACHE';
				return $tmp_path;
			} else {
				$this->compile($path);
				touch($tmp_path,$tpl_change);
				return $tmp_path;
			}
		} else {
			# Компилим
			$tmp_path=$this->compile($path);
			//touch($tmp_path,filemtime($path));
			return $tmp_path;
		}
	}
	
	# Рекурсивная функция, компилирующая блоки
	function compileBlock(&$code, $level) {
		preg_match_all('|<!--([\s]*)begin([\s]*):([\s]*)([a-zA-Z0-9\.]*)([\s]*)-->(.*)<!--([\s]*)end([\s]*):([\s]*)\4([\s]*)-->|Uis',$code,$matches);
		if ($level==0)
			$prefix = '$this->';
		else
			$prefix = '$value_'.($level -1);
		foreach ($matches[0] as $key=>$str) {
			$var=explode(".",$matches[4][$key]);
			foreach ($var as $num=>$i) {
				if ($num!=0 || $level!=0) $var[$num]='[\''.$var[$num].'\']';
			}
			$blockname=$var[0];
			$var = implode('',$var);
			$out = '<?php if (!empty('.$prefix.$var.')) foreach('.$prefix.$var.' as $value_'.$level.') { ?>';
			$in_code=$matches[6][$key];
			# Компилируем блоки внутри блока
			$this->compileBlock($in_code, $level+1);
			# Компилируем переменные внутри блока
			preg_match_all('|{([a-zA-Z_0-9\.]*)}|Uis',$in_code,$in_matches);
			foreach ($in_matches[1] as $k=>$var) {
				//echo $var.'<br />'.$matches[4][$key].'<br />'.'<hr>';
				
				if (strpos($var,$matches[4][$key].'.')===(int)0) {
					$var=substr($var,strlen($matches[4][$key].'.'));
					$thisvar=true;
				} else $thisvar=false;
				$var=explode(".",$var);
				foreach ($var as $num=>$i) {
					if ($num!=0) $var[$num]='[\''.$var[$num].'\']';
				}
				
				if ($thisvar) $var[0]='$value_'.$level.'[\''.$var[0].'\']';
				else $var[0]='$this->'.$var[0];
				$var=implode('',$var);
				$newstr='<?php echo '.$var.';?>';
				$in_code=str_replace($in_matches[0][$k],$newstr,$in_code);
			}
			# Компилирование условий
			preg_match_all('|<!--([\s]*)if([\s]*):([\s]*)([a-zA-Z0-9\.]*)([\s]*)-->(.*)<!--([\s]*)end([\s]*):([\s]*)\4([\s]*)-->|Uis',$in_code,$in_matches);
			foreach ($in_matches[0] as $k=>$str) {
				//die($var);
				echo $in_matches[0][$key].'<br />'.$in_matches[4][$key].'|'.$blockname.'.<br>';
				
				if (strpos($in_matches[4][$key], $blockname.'.')===(int)0) {
					$var=explode(".",substr($in_matches[4][$key],strlen($blockname.'.')));
					
					foreach ($var as $num=>$i) {
						$var[$num]='[\''.$var[$num].'\']';
					}
					$newstr='<?php if ($value_'.$level.implode('',$var).') { ?>'.$in_matches[6][$k].'<?php } ?>';
					$in_code=str_replace($str, $newstr, $in_code);
				}
			}
			
			$out.=$in_code;
			$out.='<?php } ?>';
			$code=str_replace($str,$out,$code);
		}
	}
	
	function compile($code) {
		# Шаг №1
		# Спрячем экранированные скобки, чтобы серьёзно  облегчить себе жизнь
		$code=str_replace(array('\{','\}'),array('&bseparator;','&eseparator;'),$code);
		
		# Шаг №2
		# Компилирование блоков
		$this->compileBlock($code, 0);
		
		# Шаг №3
		# Компилирование функций
		# Вид: {somefunc}some text{/somefunc}
		preg_match_all('|{([a-zA-Z_0-9]*)}([^{}]*){/([a-zA-Z_0-9]*)}|Uis',$code,$matches);
		
		foreach ($matches[1] as $k=>$func) {
			if (!in_array($func,$this->methods) && function_exists($func)) {
				// Данная функция есть в окружении
				$newstr='<?php echo '.$func.'(\''.str_replace("'","\'",$matches[2][$k]).'\');?>';
				$code=str_replace($matches[0][$k],$newstr,$code);
			} else {
				// Данная функция есть в переданном классе
				$newstr='<?php echo $this->'.$func.'(\''.str_replace("'","\'",$matches[2][$k]).'\');?>';
				$code=str_replace($matches[0][$k],$newstr,$code);
			}
		}
		
		# Шаг №4
		# Компилирование условий, кроме тех что относятся к блокам
		# Вид: <!--if:some-->some text<!--end:some-->
		preg_match_all('|<!--([\s]*)if([\s]*):([\s]*)([a-zA-Z0-9\.]*)([\s]*)-->(.*)<!--([\s]*)end([\s]*):([\s]*)\4([\s]*)-->|Uis',$code,$matches);
		//print_pre($matches);
		foreach ($matches[0] as $k=>$str) {
			$var=explode(".",$matches[4][$k]);
			foreach ($var as $num=>$i) {
				if ($num!=0) $var[$num]='[\''.$var[$num].'\']';
			}
			$newstr='<?php if ($this->'.implode('',$var).') { ?>'.$matches[6][$k].'<?php } ?>';
			$code=str_replace($str, $newstr, $code);
		}
		
		
		# Шаг №4
		# Компилирование контента
		# Вид: {content}
		$code=preg_replace('|{content}|Uis','<?php $this->content();?>',$code);
		
		# Шаг №5
		# Компилирование переменных
		# Вид: {varname}
		preg_match_all('|{([a-zA-Z_0-9\.]*)}|Uis',$code,$matches);
		foreach ($matches[1] as $k=>$var) {
			$var=explode(".",$var);
			foreach ($var as $num=>$i) {
				if ($num!=0) $var[$num]='[\''.$var[$num].'\']';
			}
			$var=implode('',$var);
			$newstr='<?php echo $this->'.$var.';?>';
			$code=str_replace($matches[0][$k],$newstr,$code);
		}		
		
		# Шаг №6
		# Вернём экранированные скобки
		$code=str_replace(array('&bseparator;','&eseparator;'),array('\{','\}'),$code);
		
		# Шаг №7
		# Добавление панели
		$code=preg_replace('|<([\s]*)body([^<>]*)>|Uis','\0 '."\n".'<?php $this->panel();?>',$code);
		
		# Шаг №8
		# Компилирование инклудов
		preg_match_all('|<!--([\s]*)([a-zA-Z]*)([\s]*):([\s]*)([a-zA-Z0-9_\./\\\]*)([\s]*)-->|Uis',$code,$matches);
		foreach ($matches[0] as $k=>$str) {
			switch ($matches[2][$k]) {
				case 'include':
					$out='<?php $this->render(\''.$matches[5][$k].'\'); ?>';
				break;
			}
			$code=str_replace($str,$out,$code);
		}
		
		# Возвращаем откомпиленный шаблон во временную папку
		return $code;
	}
}
?>
